package readwrite;

import lock.ThreadUtil;

import java.util.concurrent.*;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * 使用可降级机制, 来实现更新缓存后的锁降级, 保证线程安全
 * 
 * @author insight
 * @since 2021/9/13
 */
public class CacheWithLockRelegate {
    private int value = Integer.MAX_VALUE;
    public volatile boolean isUpdate;
    private ReentrantReadWriteLock rw = new ReentrantReadWriteLock();
    private Lock r = rw.readLock();
    private Lock w = rw.writeLock();

    public static void main(String[] args) throws InterruptedException {
        // 因为设置了 isUpdate, 这里使用两个线程即可, 再多线程后续也没办法执行写操作
        int thread = 2;
        CountDownLatch start = new CountDownLatch(1);
        CountDownLatch end = new CountDownLatch(thread);
        CacheWithLockRelegate cache = new CacheWithLockRelegate();

        for (int i = 0; i < thread; i++) {
            int finalI = i;
            new Thread(() -> {
                try {
                    start.await();
                    ThreadUtil.second(finalI);
                    cache.isUpdate = false;
                    System.out.println("Execute " + Thread.currentThread().getName());
                    // cache.updateWithRelegate(finalI);
                    cache.updateWithoutRelegate(finalI);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    end.countDown();
                    System.out.println("end: " + end.getCount());
                }
            }, "Thread-" + finalI).start();
        }

        start.countDown();
        end.await();
    }
    
    public void updateWithRelegate(int num) {
        r.lock();

        if (!isUpdate) {
            // release read lock before using write lock
            r.unlock();
            w.lock();
            try {
                // double check
                if (!isUpdate) {
                    value = num;
                    isUpdate = true;
                    System.out.println(Thread.currentThread().getName() + " updated value!");
                }
                // 降级
                r.lock();
            } finally {
                w.unlock();
            }
        }
        
        try {
            // 留出时间 给其他线程尝试修改 由于使用了降级 可保证其他线程写锁会阻塞
            ThreadUtil.second(5);
            System.out.println(Thread.currentThread().getName() + ": " +  value);
        } finally {
            if (rw.getReadHoldCount() > 0) {
                r.unlock();
            }
        }
    }

    public void updateWithoutRelegate(int num) {
        r.lock();

        if (!isUpdate) {
            // release read lock before using write lock
            r.unlock();
            w.lock();
            try {
                // double check
                if (!isUpdate) {
                    System.out.println(Thread.currentThread().getName() + " updated value!");
                    value = num;
                    isUpdate = true;
                }
            } finally {
                w.unlock();
            }
        }

        try {
            // 留出时间 给其他线程尝试修改 没使用降级 不保证感知数据修改
            ThreadUtil.second(5);
            System.out.println(Thread.currentThread().getName() + ": " +  value);
        } finally {
            if (rw.getReadHoldCount() > 0) {
                r.unlock();
            }
        }
    }
}